package thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author ycz
 * @date 2021/6/15
 *
 *  =============================
 *  synchronized 和 lock 的区别
 *  1.
 *  synchronized是Java的关键字，底层通过monitor（monitorexter、monitorexit）对象来完成，其wait/notify等方法也依赖于monitor对象只有在同步代码块中才能调wait/notify等方法
 *  lock是具体类，是api层面的锁
 *  2.使用方法
 *  synchronized不需要用户手动释放锁，当同步代码块执行完后主动让线程释放对锁的占用
 *  reentranlock需要用户手动释放锁，若没有主动释放锁，有可能导致死锁
 *  3.等待是否可终端
 *  synchronized不可中断，除非抛出异常或者正常执行完
 *  reentrantlock可中断
 *  4.加锁是否公平
 *  reentranlock两者都可以，默认非公平锁
 *  synchronized非公平锁
 *  5.锁绑定多个条件condition,synchroinzed没有
 *  synchronized要么随机唤醒一个，要么唤醒全部
 *  ReentrantLock用来实现分组唤醒需要唤醒的线程，可以精确唤醒，
 *
 *
 */
public class ReetrantLockTest {

    /**
     *  一个初始值为0的变量，两个线程对其操作 一个加1，一个减1，重复5次
     *  思路：
     *  线程 ==》 操作（方法） ==》 资源类
     *  判断 ==》 干活 ==》 通知
     *  防止虚假唤醒机制
     */
    static class ShareData{
        private int number = 0;
        private Lock lock= new ReentrantLock();
        private Condition condition=lock.newCondition();
        public void increment() throws InterruptedException {
            lock.lock();
            try {
                // 1.判断
                while (number!=0){
                    condition.await();
                }
                // 2.干活
                number++;
                System.out.println(Thread.currentThread().getName()+"=="+"number=="+number);
                // 通知唤醒
                condition.signalAll();
            } catch (InterruptedException e) {
            } finally {
                lock.unlock();
            }
        }

        public void decrement(){
            lock.lock();
            try {
                while (number==0){
                    condition.await();
                }
                number--;
                System.out.println(Thread.currentThread().getName()+"=="+"number=="+number);
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * 多线程之间按顺序调用，实现a->b->c 三个线程启动(利用condition)
     * a打印5次，b打印10次，c打印15次
     */
    static class ShareResource{
        private int number=1;
        private Lock ll=new ReentrantLock();
        private Condition c1 = ll.newCondition();
        private Condition c2 = ll.newCondition();
        private Condition c3 = ll.newCondition();

        public void print5(){
            ll.lock();
            try {
                // 1.判断
                while (number!=1){
                    c1.await();
                }
                // 2.干活
                for (int i=0;i<5;i++){
                    System.out.println(Thread.currentThread().getName()+"=="+"i=="+i);
                }
                // 3.通知
                number=2;
                c2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                ll.unlock();
            }
        }

        public void print10(){
            ll.lock();
            try {
                // 1.判断
                while (number!=2){
                    c2.await();
                }
                // 2.干活
                for (int i=0;i<5;i++){
                    System.out.println(Thread.currentThread().getName()+"=="+"i=="+i);
                }
                // 3.通知
                number=3;
                c3.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                ll.unlock();
            }
        }

        public void print15(){
            ll.lock();
            try {
                // 1.判断
                while (number!=3){
                    c3.await();
                }
                // 2.干活
                for (int i=0;i<5;i++){
                    System.out.println(Thread.currentThread().getName()+"=="+"i=="+i);
                }
                // 3.通知
                number=1;
                c1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                ll.unlock();
            }
        }

    }

    public static void test1(){
        ShareData shareData = new ShareData();
        new Thread(()->{
            for (int i=0;i<5;i++){
                try {
                    shareData.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"a").start();
        new Thread(()->{
            for (int i=0;i<5;i++){
                try {
                    shareData.decrement();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"b").start();
        new Thread(()->{
            for (int i=0;i<5;i++){
                try {
                    shareData.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"c").start();
        new Thread(()->{
            for (int i=0;i<5;i++){
                try {
                    shareData.decrement();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"d").start();
    }

    public static void test2(){
        ShareResource shareResource = new ShareResource();
        new Thread(()->{
            for (int i=0;i<5;i++){
                try {
                    shareResource.print5();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"a").start();
        new Thread(()->{
            for (int i=0;i<10;i++){
                try {
                    shareResource.print10();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"b").start();
        new Thread(()->{
            for (int i=0;i<5;i++){
                try {
                    shareResource.print15();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"c").start();
    }

    public static void main(String[] args) {
//        test1();
        test2();
    }

}
